The Definitive Guide To The Third Dimension (Part 1)
Introduction
Everything is 3D now. Even computer games that look 2D have some 3D rotational stuff in it, and I can't remember the last demo that had purely 2D effects in it. To the newbie coder, this can be pretty scary, and I think that one of the reasons for lack of new coders in the scene is due to this technically difficult beast putting them off. This is why I am writing this series of articles. I hope to cover all the main topics and effects used today to help new coders, Plotting and Rotating 3D, Texture Mapping, Gouraud Shading, Phong Shading, lighting effects, etc.
Part 1 of this series is a basic primer to the world of 3D coding. I am going to take it very slowly to ensure maximum understanding on your part. I am also going to assume complete 3D ignorance on your part but a firm understanding on basic 2D principles (putting a pixel onto the screen, drawing a line, etc).
Part 1 - An introduction to 3D
Up till now you have been coding in 2D, a sprite moving around the screen or whatever, with two axis. The X and Y axis, with the X axis increasing horizontally from your left hand monitor edge to the right and the Y axis increasing from the top downwards (fig 1).
Figure 1:
³
³
³
³
-----------0-----------> +X
³
³
³
³
\ /
+Y
In 3D we have to consider another axis, a third axis which increases outwards into the screen (fig 2).
Figure 2:
³ / +Z
³ /
³ /
³/
-----------0-----------> +X
/³
/ ³
/ ³
/ ³
/ \ /
-Z +Y
It is on this new coordinate system that we shall now be working. In your 3D world you are going to have to decide where you should put (0,0,0). It doesn't strictly matter where you put this, it is purely a question of taste.
In a typical 3D world, there are three main things that should be taken into consideraton. The Objects, The Camera and The World. The world is where all things are contained. The objects are the things in your world, everything from a fly to king-kong are objects. The camera is the where the viewer of your demo will be looking through onto your world.
Let's say I have a simple 3D scene in my demo. Say, a person sitting in a garden by a house with the viewer of my demo looking at it from outside the house's front door. The world is the land where the house and the garden are situated, the objects are the person and the house, and the camera is outside the house. It looks something like fig 3.
Figure 3:
Ú----------------------------------------¿
³ ³ <-- Land
³ ³
³ O <-- Person sitting in garden ³
³ ³
³ /\ <-- Camera ³
³ Ú-----¿ ³
³ ³ ³ <-- House ³
³ À------ ³
À-----------------------------------------
If the house is at (0,0,0) in the world then the camera is at (0,0,10) and the person is at (-10,0,20). If X increases left to right, Y increases into the sky and X increases along the garden. Geddit? If I chose to have a teradactil fly over the house, then it would be at point (0,10,0).
Using the coordinate system like this is called a 'World Coordinate System'.
So, how do we define an object?
We define objects using what is called the 'Local Coordinate System'. It would be ridiculous to have every vertex defined using the World Coordinate System, things would get too complicated. Imagine if every polygon of the person in fig 3 was defined with respect to the house! Also, it would be hard to implement the person in any other scene.
This is how the LCS works: Every vertex that makes up the object is defined as being (X,Y,Z) from a point _within the object_, usually the centre. A 3D line, using this coordinate system, would be defined as:
(-10, 0, 0) to (10, 0, 0)
for example.
Plotting your first 3D point
So now you know how 3D works, it is time to look at some code. This issue I am going to keep it simple.
Since the display is a 2D thing, a 3D point must be converted to 2D coordinates:
screenx = ((256*x) / (z-objZ)) + objX;
screeny = ((256*y) / (z-objZ)) + objY;
where x,y and z are the coordinates of the 3D point to be converted and objX, objY and objZ are the coordinates of the object that the point is part of.
So, if I wanted to plot my above 3D line onto the middle of a 320x200 screen 160 pixels into the distance I would do the following:
void DrawMy3DLine()
{
DrawLine(
((256*-10) / (0-160)) + 160, ((256*0) / (0-160)) + 100,
((256* 10) / (0-160)) + 160, ((256*0) / (0-160)) + 100
);
}
Ok, let's examine the above. The DrawLine function takes the format of:
DrawLine(x1,y1, x2,y2);
Drawing a 2D line from (x1,y1) --> (x2,y2). What I have done in the function is convert my 3D lines coordinates (x1,y1,z1) --> (x2,y2,z2) into 2D coordiantes for use in my function.
3D Rotation
Having a 3D object is not much use in a demo unless it can be rotated. The maths theory behind the following equations is quite complicated to do with multiplying matrices. You can either do that stuff or you can't so I am going to give you the equations now and for you mathematicians I have appended the equations at the end of this document.
You can rotate a 3D object about any of the three axes, varying degrees on each axis at once can produce any rotation.
Rotation around the Z axis:
nx=cos (a)*x - Sin (a)*y
ny=sin (a)*x + Cos (a)*y
nz=z
Rotation around the Y axis:
nx=cos (a)*x - Sin (a)*z
ny=y
nz=sin (a)*x + Cos (a)*z
Rotation around the X axis:
nx=x
ny=cos (a)*y - Sin (a)*z
nz=sin (a)*y + Cos (a)*z
A simple rotate routine using these equations would be:
void RotatePoint(int x, int y, int z, int xdeg, int ydeg, int zdeg)
{
int tx,ty,tz;
// Rotation around the Z axis
tx=cos (zdeg)*x - sin (zdeg)*y;
ty=sin (zdeg)*x + cos (zdeg)*y;
tz=z;
x=tx; y=ty; z=tz;
// Rotation around the Y axis
tx=cos (ydeg)*x - sin (ydeg)*z;
ty=y;
tz=sin (ydeg)*x + cos (ydeg)*z;
x=tx; y=ty; z=tz;
// Rotation around the X axis
tx=x;
ty=cos (xdeg)*y - sin (xdeg)*z;
tz=sin (xdeg)*y + cos (xdeg)*z;
x=tx; y=ty; z=tz;
}
This routine simply takes the point you enter in and rotates it in each direction in turn, one after the other. DON'T use this in your demos. Keep a base object as a constant and rotate that into a temp variable.
That is basically 3D. Next time, solid objects and we'll start on our 3D graphics engine.
In closing
I hope this has helped you get to grips with the foundations of 3D programming. E-mail me at godlike2@hotmail.com if you have any further questions. Your homework is to write a short routine to rotate a line cube. E-mail it to me, I'd love to have a look! Also, e-mail me your code if you cannot get it to work and I'll do the best I can to help you. As promised, here are those matrices... enjoy!
C U
- STDIO.H (godlike2@hotmail.com)
Matrix for rotation around the Z axis:
[ Cos (a) -Sin (a) 0 0 ] [ x ]
[ Sin (a) Cos (a) 0 0 ] . [ y ]
[ 0 0 1 0 ] [ z ]
[ 0 0 0 1 ] [ 1 ]
Matrix for rotation around the Y axis:
[ Cos (a) 0 -Sin (a) 0 ] [ x ]
[ 0 1 0 0 ] . [ y ]
[ Sin (a) 0 Cos (a) 0 ] [ z ]
[ 0 0 0 1 ] [ 1 ]
Matrix for rotation around the X axis:
[ 1 0 0 ] [ x ]
[ 0 Cos (a) -Sin (a) 0 ] . [ y ]
[ 0 Sin (a) Cos (a) 0 ] [ z ]
[ 0 0 0 1 ] [ 1 ]
[ 0 0 0 1 ] [ 1 ]